package user

import (
	"fmt"

	"github.com/Masterminds/squirrel"
	"github.com/gin-gonic/gin"
	"go-api-template/api/http"
	"go-api-template/api/models/system"
	"go-api-template/api/ws/front"
	"go-api-template/internal/e"
	"go-api-template/service"
)

func (req *OnlineListReq) Do(ctx *gin.Context, s *service.Service) (resp http.Resp, err error) {
	var data OnlineUsersResp

	data.Items = make([]OnlineUsersRespItem, 0)

	OnlineClientListRespM := map[string][]front.ClientData{}

	for c, _ := range front.HUB.C {
		if v, ok := OnlineClientListRespM[c.Data.UserId]; ok {
			OnlineClientListRespM[c.Data.UserId] = append(v, *c.Data)
		} else {
			OnlineClientListRespM[c.Data.UserId] = []front.ClientData{*c.Data}
		}
	}
	for k, v := range OnlineClientListRespM {
		data.Items = append(data.Items, OnlineUsersRespItem{
			UserId:  k,
			Clients: v,
		})
	}

	resp.SetData(data)
	return
}

func (req *SaveReq) Do(_ *gin.Context, s *service.Service) (resp http.Resp, err error) {
	var u system.User
	var data SaveResp

	// 创建、更新
	if req.UserId == "" {
		// 手机号、邮箱 唯一
		if req.Phone != 0 || req.Email != "" {
			if err = s.Db.Where("phone = ?", req.Phone).Or("email = ?", req.Email).Find(&u).Error; err != nil {
				return
			}
			if u.ID != "" {
				resp.SetCode(e.PhoneOrEmailRepect)
				return
			}
		}
		u = system.User{
			Username: req.Username,
			Email:    req.Email,
			Phone:    req.Phone,
			Password: req.Password,
		}
		s.Db.Create(&u)
	} else {
		s.Db.Where("user_id = ?", req.UserId).First(&u)
		if u.ID == "" {
			resp.SetCode(e.UserNotFound)
			return
		}
		// 手机号、邮箱 唯一
		if req.Phone != 0 || req.Email != "" {
			if err = s.Db.Where("phone = ?", req.Phone).Or("email = ?", req.Email).Find(&u).Error; err != nil {
				return
			}
			if u.ID != "" && u.ID != req.UserId {
				service.Log.Debug(u.ID)
				resp.SetCode(e.PhoneOrEmailRepect)
				return
			}
		}
		s.Db.Model(&u).
			Update("username", req.Username).
			Update("email", req.Email).
			Update("phone", req.Phone).
			Update("password", req.Password)
	}

	data.ID = u.ID

	resp.SetData(data)
	return
}

func (req *ListReq) Do(_ *gin.Context, s *service.Service) (resp http.Resp, err error) {
	var data ListResp
	var users []system.User

	selectS := `*`
	req.query(selectS, s, &users)
	if !req.IsExport {
		data.Total = req.query("count(1)", s, nil)
	}

	var userIds []string
	var userRoles []struct {
		RoleId   string `gorm:"column:role_id"`
		RoleName string `gorm:"column:role_name"`
		UserId   string `gorm:"column:user_id"`
	}
	userRolesM := make(map[string][]ListRespItemRole)
	for _, user := range users {
		userIds = append(userIds, user.ID)
	}
	if len(userIds) > 0 {
		userRolesSq := squirrel.
			Select([]string{
				"sur.user_id",
				"sur.role_id",
				"sr.name as role_name",
			}...).
			From("system_user_role sur").
			LeftJoin("system_role sr on sr.id = sur.role_id").
			Where("sur.user_id in ?", userIds)

		toSql, value, _ := userRolesSq.ToSql()
		s.Db.Raw(toSql, value...).Scan(&userRoles)
		for _, userRole := range userRoles {
			if v, ok := userRolesM[userRole.UserId]; ok {
				userRolesM[userRole.UserId] = append(v, ListRespItemRole{
					RoleId:   userRole.RoleId,
					RoleName: userRole.RoleName,
				})
			} else {
				userRolesM[userRole.UserId] = []ListRespItemRole{{userRole.RoleId, userRole.RoleName}}
			}
		}
	}

	for _, user := range users {
		data.Items = append(data.Items, ListRespItem{
			ID:          user.ID,
			Name:        user.Username,
			Email:       user.Email,
			AllowDelete: user.Username != "admin",
			IsAdmin:     user.IsAdmin == 1,
			Roles:       userRolesM[user.ID],
		})
	}

	resp.SetData(data)
	return
}

func (req *ListReq) query(selectS string, s *service.Service, items *[]system.User) (count int) {
	itemsSq := squirrel.Select(selectS).
		From("system_user")

	if req.Username != "" {
		itemsSq = itemsSq.Where("username like ?", fmt.Sprint("%", req.Username, "%"))
	}

	if req.Email != "" {
		itemsSq = itemsSq.Where("email like ?", fmt.Sprint("%", req.Email, "%"))
	}

	if items == nil {
		sql, value, _ := itemsSq.ToSql()
		s.Db.Raw(sql, value...).Scan(&count)
	} else {
		if !req.IsExport {
			if req.PageSize != 0 && req.Page != 0 {
				itemsSq = itemsSq.Limit(uint64(req.PageSize)).Offset(uint64(req.PageSize * (req.Page - 1)))
			}
		}
		sql, value, _ := itemsSq.ToSql()
		s.Db.Raw(sql, value...).Scan(items)
	}
	return
}

func (req *SetAdminReq) Do(ctx *gin.Context, s *service.Service) (resp http.Resp, err error) {
	var data SetAdminResp
	var u system.User
	s.Db.Model(&system.User{}).Where("id = ?", req.UserId).Scan(&u)
	if u.ID == "" {
		resp.SetCode(e.UserNotFound)
		return
	}

	if u.IsAdmin == 1 {
		s.Db.Model(&u).Update("is_admin", 2)
	} else {
		s.Db.Model(&u).Update("is_admin", 1)
	}

	resp.SetData(data)
	return
}
